import { isRegistered, register, unregister } from "@tauri-apps/api/globalShortcut";
import { FopApi } from "@/lib/api";
import { type ShortcutState, type ShortcutItem, isShortcutState } from "./types";
import { KeyCode, stringAsKeyCode, convertShortcutStringToKeyCode, convertKeyCodeToShortcutString } from "../keycode";

/**
 * Shortcut 命名空间，内部均为对 ShortcutState 和 ShortcutItem 的处理函数
 */
export class Shortcut {
    /**
     * 生成一个新的 ShortcutState
     * @returns 新的 ShortcutState
     */
    public static state(): ShortcutState {
        return {
            ctrl: false,
            alt: false,
            shift: false,
            meta: false,
            keyCode: KeyCode.Unidentified,
        };
    }

    /**
     * 生成一个新的 ShortcutItem
     * @returns 新的 ShortcutItem
     */
    public static item(): ShortcutItem {
        return {
            used: false,
            name: "",
            shortcut: Shortcut.state(),
            alternative: Shortcut.state(),
            fileName: "",
            description: "",
            handler: () => {},
        };
    }

    /**
     * 检查快捷键状态是否相等
     * @param shortcutState1 快捷键状态 1
     * @param shortcutState2 快捷键状态 2
     * @returns 快捷键状态 1 是否与 快捷键状态 2 相等
     */
    public static isStateEqual(shortcutState1: ShortcutState, shortcutState2: ShortcutState): boolean {
        return (
            shortcutState1.ctrl === shortcutState2.ctrl &&
            shortcutState1.alt === shortcutState2.alt &&
            shortcutState1.shift === shortcutState2.shift &&
            shortcutState1.meta === shortcutState2.meta &&
            shortcutState1.keyCode === shortcutState2.keyCode
        );
    }

    /**
     * 检查快捷键是否可用
     * @param shortcutState 快捷键状态
     * @returns 是否可用
     */
    public static isAvailableState(shortcutState: ShortcutState): boolean {
        return (
            shortcutState.alt ||
            shortcutState.ctrl ||
            shortcutState.shift ||
            shortcutState.meta ||
            shortcutState.keyCode !== KeyCode.Unidentified
        );
    }

    /**
     * 从键盘事件中获取快捷键
     * @param event 键盘事件对象
     * @returns 快捷键
     */
    public static stateFromEvent(event: KeyboardEvent): ShortcutState {
        return {
            ctrl: event.ctrlKey,
            alt: event.altKey,
            shift: event.shiftKey,
            meta: event.metaKey,
            keyCode: stringAsKeyCode(event.code),
        };
    }

    /**
     * 从字符串中获取快捷键
     * @param shortcut 字符串
     * @returns 快捷键
     */
    public static stateFromString(shortcut: string): ShortcutState {
        // 返回结果
        const res = Shortcut.state();

        // 分割 shortcut 各部分
        const parts = shortcut.split("+");

        // 如果 shortcut 为空，则返回默认 state
        if (parts.length === 0) {
            return res;
        }

        // 遍历功能键（前面部分均视为功能键）
        for (let i = 0; i < parts.length - 1; i++) {
            // 去除空格并转换为大写
            const part = parts[i].trim().toUpperCase();

            // 尝试匹配
            switch (part) {
                case "CTRL":
                case "CONTROL":
                case "CMD":
                case "COMMAND":
                case "CMDORCONTROL":
                case "COMMANDORCONTROL":
                    res.ctrl = true;
                    break;
                case "ALT":
                case "MENU":
                    res.alt = true;
                    break;
                case "SHIFT":
                    res.shift = true;
                    break;
                case "WIN":
                case "META":
                    res.meta = true;
                    break;
                default:
                    parts[i] = "";
            }
        }

        // 最后一个按键视为键盘按键
        // 遍历所有可能的按键，找到匹配的按键
        // 从最长的按键开始匹配
        const key = parts[parts.length - 1].trim();
        for (let i = key.length; i > 0; i--) {
            const keyCode = convertShortcutStringToKeyCode(key.substring(0, i));
            if (keyCode !== KeyCode.Unidentified) {
                res.keyCode = keyCode;
                break;
            }
        }

        return res;
    }

    /**
     * 根据快捷键状态获取快捷键字符串
     * @param shortcutState 快捷键状态
     * @param onlyKeyCode 是否只显示 KeyCode 字符串
     * @returns 快捷键状态字符串
     */
    public static stateToString(shortcutState: ShortcutState, onlyKeyCode: boolean = false): string {
        // 只显示 KeyCode 值
        if (onlyKeyCode) {
            const res = convertKeyCodeToShortcutString(shortcutState.keyCode);
            if (res.toUpperCase() === "PLUS") {
                return "+";
            }
            return res;
        }

        // 结果数组
        const res: string[] = [];

        // Ctrl
        if (
            shortcutState.ctrl &&
            shortcutState.keyCode !== KeyCode.ControlLeft &&
            shortcutState.keyCode !== KeyCode.ControlRight
        ) {
            res.push("CmdOrCtrl");
        }

        // Alt
        if (
            shortcutState.alt &&
            shortcutState.keyCode !== KeyCode.AltLeft &&
            shortcutState.keyCode !== KeyCode.AltRight
        ) {
            res.push("Alt");
        }

        // Shift
        if (
            shortcutState.shift &&
            shortcutState.keyCode !== KeyCode.ShiftLeft &&
            shortcutState.keyCode !== KeyCode.ShiftRight
        ) {
            res.push("Shift");
        }

        // Meta
        if (shortcutState.meta) {
            res.push("Super");
        }

        // Key
        if (shortcutState.keyCode !== KeyCode.Unidentified) {
            res.push(convertKeyCodeToShortcutString(shortcutState.keyCode));
        }

        // 拼接结果
        return res.join("+");
    }

    /**
     * 加载快捷键
     * @param shortcutItem 快捷键
     */
    public static async load(shortcutItem: ShortcutItem) {
        // 读取本地快捷键
        if (shortcutItem.fileName !== "") {
            try {
                const item = await FopApi.readJson<ShortcutState>(`shortcut/${shortcutItem.fileName}`);
                if (item !== undefined && isShortcutState(item)) {
                    shortcutItem.shortcut = item;
                } else {
                    console.warn(`不合法的快捷键文件 ${shortcutItem.fileName}`, item);
                }
            } catch (error) {
                console.warn("读取本地快捷键", shortcutItem.name, "配置失败", error);
            }
        }

        // 使用备选键
        if (!Shortcut.isAvailableState(shortcutItem.shortcut) && Shortcut.isAvailableState(shortcutItem.alternative)) {
            shortcutItem.shortcut = shortcutItem.alternative;
        }
    }

    /**
     * 保存快捷键
     * @param shortcutItem 快捷键
     */
    public static async save(shortcutItem: ShortcutItem) {
        // 保存本地快捷键
        if (shortcutItem.fileName !== "") {
            try {
                if (!Shortcut.isAvailableState(shortcutItem.shortcut)) {
                    await FopApi.removeJson(`shortcut/${shortcutItem.fileName}`);
                } else {
                    await FopApi.writeJson(`shortcut/${shortcutItem.fileName}`, shortcutItem.shortcut);
                }
            } catch (error) {
                console.warn("保存本地快捷键", shortcutItem.name, "配置失败", error);
            }
        }
    }

    /**
     * 启用快捷键
     * @param shortcutItem 快捷键
     */
    public static async enable(shortcutItem: ShortcutItem) {
        // 没有快捷键
        if (!Shortcut.isAvailableState(shortcutItem.shortcut)) {
            return;
        }

        // 获取快捷键字符串
        const shortcutString = Shortcut.stateToString(shortcutItem.shortcut);

        // 已启用
        if (await isRegistered(shortcutString)) {
            return;
        }

        // 启用快捷键
        try {
            await register(shortcutString, shortcutItem.handler);
            shortcutItem.used = true;
        } catch (error) {
            try {
                await unregister(shortcutString);
            } catch {}

            console.warn("启用快捷键", shortcutItem.name, "失败", error);
        }
    }

    /**
     * 禁用快捷键
     * @param shortcutItem 快捷键
     */
    public static async disable(shortcutItem: ShortcutItem) {
        // 已禁用
        if (!shortcutItem.used) {
            return;
        }

        // 获取快捷键字符串
        const shortcutString = Shortcut.stateToString(shortcutItem.shortcut);

        // 禁用快捷键
        if (await isRegistered(shortcutString)) {
            try {
                await unregister(shortcutString);
                shortcutItem.used = false;
            } catch (error) {
                console.warn("禁用快捷键", shortcutItem.name, "失败", error);
            }
        }
    }
}
